

#include <iostream>
#include <memory>
#include <sys/epoll.h>
#include <functional>
#include "Socket.hpp"
#include "InetAddr.hpp"
#include "Connection.hpp"
#include "Reactor.hpp"

using func_t = std::function<std::string(std::string &)>;

//定位：只负责IO
class IOService : public Connection
{
    static const int size = 10;


public:
    IOService(int sockfd)
    {
        SetNonBlock(sockfd);
        SetSockfd(sockfd);
        SetEvents(EPOLLIN | EPOLLET);
    }
    virtual void Sender() override
    {
        //直接写
        while(true)
        {
            ssize_t n = send(Fd(),OutString().c_str(),OutString().size(),0);
            //上面是实际发送数
            if(n>0)
            {
                //成功
                DisCardOutString(n);//移除n个
            }
            else if(n==0)
            {
                //我们认为发完了
                break;
            }
            else
            {
                //小于0错粗了
                if(errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    //写满了，下次再来
                    break;
                }
                else if(errno == EINTR)
                {
                    //被中断了
                    continue;
                }
                else{
                    //出错了
                    Excepter();
                    return;
                }
            }
        }
        //走到这只有两种情况
        //1.outbuffer empty空了。
        //2.发送缓冲区被写满了，outbuffer empty不为空。——这代表写条件不满足，添加到Epoll中
        if(!IsOutBufferEmpty())
        {
            //修改对sockfd的读事件的关心！！——开启写事件关心。
            //默认写事件是关心的
            // 多路转接的方案设计的时候，写事件关心，永远不能常开启
            //按需设置！
            GetOwner()->EnableReadWrite(Fd(),true,true);
        }
        else//没有满就不能接着监视了，不然会一直告诉你空间一直有剩余。因为send只看发送缓冲区是否有剩余，recv只看接受缓冲区是否有数据。
        {
            GetOwner()->EnableReadWrite(Fd(),true,false);//这样处理就可以按需设置了，也就是只要你的写使能是true的那么Epoll就会监视发送缓冲区是否有剩余空间，有就会去执行send方法。
        }
    }
    virtual void Recver() override
    {
        // 1.读取所有数据
        while (true)
        {
            char buffer[size];
            ssize_t s = recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
            if (s > 0)
            {
                // 成功
                buffer[s] = 0;
                AppendString(buffer);
            }
            else if (s == 0)
            {
                // 对端关闭连接，接受到0个字节我们就认为是关闭的连接，和send发了0个字节认为发送缓冲区满了一样。
                Excepter(); // 调用异常函数。
                return;
            }
            else // 返回值-1不一定错误
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    // 正常结束，因为接受缓冲区中没有数据可读了，我们就认为是正常读取结束了。(当然不排除，有丢包的可能性)
                    break;
                }
                else if (errno == EINTR)
                {
                    // 在IO拷贝时被中断，那就再来一遍
                    continue;
                }
                else
                {
                    Excepter(); // 出错了就统一处理。
                }
            }
        }
        // 这里就读完了数据！
        std::cout << "收到的数据#" << InString() << std::endl;
        // 这里我们还需要加协议，来确保报文的完整性。
        // 下面是通过协议解析报文。 
        std::string result;
        if (_on_message)
           result =  _on_message(InString());
        //添加应答信息
        AppendOutString(result);
        // if(result.empty())
        // {
        //     //当前没有读到完整的请求
        // }
        
        //如何处理写的问题，其实就是把outbuffer发送给对方的问题！！！
        if(!IsOutBufferEmpty())
        {   

            //方案1：Sender();//推荐做法，大部分开源项目都是这么写的
            //方案2：使能Writeable即可，也就是开启写就行。
            GetOwner()->EnableReadWrite(Fd(),true,true);//自动触发了。照样能拿到
        }
    }
    void RegisterOnMessage(func_t on_message) // 注册方法接口，让外界去填入协议
    {
        _on_message = on_message;
    }
    virtual void Excepter() override
    {
        // IO读取时，所有的异常处理，全部转化成这一个函数的调用，回收之前的伏笔。
        // 出异常了怎么办？打印日志，差错处理，关闭链接，从Reactor中的异常Connection移除，还要将fd从Epoll中移除对他的关心。
        std::cout<<"客户端可能结束，进行异常处理 "<<std::endl;//这里的输出应该要被替换成日志
        GetOwner()->DelConnection(Fd());        
    }
    ~IOService() {}

private:
    func_t _on_message; // 进行应用层协议处理。//不再这个类里写协议内容，这是深耦合的做法，我们确保解耦合，可以复用，就用回调函数去处理。
};
